//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include "ExtractPDBInfo.h"
#include "Strings.h"
#include "ListE.h"

#include <sys/types.h>

#include <stdio.h>
#include <stdlib.h>

#include <string.h>
#include <vector>
#include <dirent.h>
#include <errno.h>

#include <iostream>
#include <fstream>

#include <zlib.h>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define LENGTHLINE 5000
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ExtractPDBInfo::ExtractPDBInfo () {
  
  this->location                = new String();
  this->locationProteinOut      = new String();
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ExtractPDBInfo::ExtractPDBInfo (const ExtractPDBInfo& extractPDBInfo) {
  
  this->location                = new String(extractPDBInfo.location);
  this->locationProteinOut      = new String(extractPDBInfo.locationProteinOut);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ExtractPDBInfo::ExtractPDBInfo (const ExtractPDBInfo* extractPDBInfo) {
  
  this->location                = new String(extractPDBInfo->location);
  this->locationProteinOut      = new String(extractPDBInfo->locationProteinOut);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ExtractPDBInfo::~ExtractPDBInfo () {
  
  if (this->location)           delete this->location;
  if (this->locationProteinOut) delete this->locationProteinOut;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ExtractPDBInfo::Location (String* location) {
  
  if (location) this->location->In(location);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ExtractPDBInfo::LocationProteinOut (String* locationProteinOut) {
  
  if (locationProteinOut) this->locationProteinOut->In(locationProteinOut);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ExtractPDBInfo::Location (void) {
  
  return this->location;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ExtractPDBInfo::LocationProteinOut (void) {
  
  return this->locationProteinOut;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ExtractPDBInfo::GeneratedReducedPDBDatabase () {
    
  if (!this->location           || !this->location->Length()          ||
      !this->locationProteinOut || !this->locationProteinOut->Length())
    return false;

  TListE <String>* listFolder   = new TListE <String> ();
  TListE <String>* listFilesIn  = new TListE <String> ();
  TListE <String>* listFilesOut = new TListE <String> ();
  String*          folder       = new String (); 
  
  char             buffer[LENGTHLINE + 1];
  
  this->GetDirectories(this->location, listFolder);
  listFolder->SetInitial();
  while (listFolder->SetNext()) {
    
    sprintf(buffer, "%s%s/", this->location->Out(), listFolder->GetCurrent()->Out());
    cout << "Search in folder: " << buffer << "\r";
    
    folder->In(buffer);
    
    this->GetFiles(folder, this->locationProteinOut, listFilesIn, listFilesOut);
    
  }

  cout << endl << "Reading Files..." << endl;
  cout << "---------------------------------------------------------------------------------" << endl;

  listFilesIn->SetInitial();
  listFilesOut->SetInitial();

  while (listFilesIn->GetNext() && listFilesOut->GetNext()) {

    cout << "File In: "  << listFilesIn->GetCurrent()->Out() << endl;
    cout << "File Out: " << listFilesOut->GetCurrent()->Out() << endl;
    
    gzFile reducedPDBFile = gzopen(listFilesOut->GetCurrent()->Out(), "wb9");
    gzFile pdbFile        = gzopen(listFilesIn->GetCurrent()->Out(),  "rb9");
    
    while (!gzeof(pdbFile)) {

      gzgets(pdbFile, buffer, LENGTHLINE);

      if (!strncmp(buffer, "ATOM", strlen("ATOM"))) {

        String* line   = new String(buffer);
        String* atomName = line->SubStr(12, 16);
        atomName->RTrim();
          
        if (!strcmp(atomName->Out(), "CA") || !strcmp(atomName->Out(), "CB"))
          gzprintf(reducedPDBFile, "%s\0", line->Out());

        if (atomName) delete atomName;
        if (line)     delete line;
        
      }
      
      if (!strncmp(buffer, "TER", strlen("TER"))) {
        gzprintf(reducedPDBFile, "TER\n\0");
        break;
      }

    }
    
    gzclose(pdbFile);
    gzclose(reducedPDBFile);
    
  }

  if (listFilesIn)  delete listFilesIn;
  if (listFilesOut) delete listFilesOut;
  if (listFolder)   delete listFolder;
  if (folder)       delete folder;

  return true;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ExtractPDBInfo::GetDirectories (String* dir, TListE <String>* files) {
  
  DIR*            dp;
  struct dirent*  dirp;
  
  if(!(dp  = opendir(dir->Out()))) return errno;

  while ((dirp = readdir(dp)) != NULL) 
    if (!strstr(dirp->d_name, ".")) files->Add(new String (dirp->d_name));
  
  closedir(dp);
  
  return 0;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ExtractPDBInfo::GetFiles (String* dirIn, String* dirOut, TListE <String>* filesIn, TListE <String>* filesOut) {
  
  DIR*            dp;
  struct dirent*  dirp;
  
  if(!(dp  = opendir(dirIn->Out()))) return errno;

  while ((dirp = readdir(dp)) != NULL) {
    
    if (!strcmp(dirp->d_name, ".") || !strcmp(dirp->d_name, "..")) continue;
    
    String* newPathFileIn   = new String (dirIn);
    String* newPathFileLOut = new String (dirOut);
    
    newPathFileIn->Add(dirp->d_name);
    newPathFileLOut->Add(dirp->d_name);
    
    filesIn->Add(newPathFileIn);
    filesOut->Add(newPathFileLOut);
    
  }
  
  closedir(dp);
  
  return 0;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ExtractPDBInfo ExtractPDBInfo::operator= (const ExtractPDBInfo& extractPDBInfo) {
  
  this->location->In(extractPDBInfo.location);
  this->locationProteinOut->In(extractPDBInfo.locationProteinOut);
  
  return *this;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//

